﻿using System;
using Brouteforce.utilities;
using Microsoft.Xna.Framework;

namespace Brouteforce.OldStuff.oldGameObjs {
    public class Location {
        public Rectangle Position;
        public Vector2 RealPosition;
        public Vector2 Velocity;
        private Vector2? _waypoint = null;

        public Location(Rectangle position) {
            FixLocation(position);
        }

        public Location(int x, int y, int width, int height) {
            FixLocation(new Rectangle(x, y, width, height));
        }

        public int OnScreenX { get { return Position.X; } }
        public int OnScreenY { get { return Position.Y; } }

        /// <summary>
        /// return the location on the screen relative to the location of the camera
        /// for example: 
        /// if your full virtual screen (all the game screen) is 1000X1000 pixels 
        /// but your camera is located on 10X10 
        /// and if your location on the game screen is 500X500 then your relative location is 
        /// 490X490 cause the camera are shifting right the first 10X10 pixels
        /// </summary>
        /// <param name="cameraX"></param>
        /// <param name="cameraY"></param>
        /// <returns></returns>
        public Rectangle GetPositionRelativeToCamera(int cameraX, int cameraY) {
            return new Rectangle(Position.X - cameraX, Position.Y - cameraY, Position.Width, Position.Height);
        }

        public Vector2 PositionVector {
            get { return new Vector2(Position.X, Position.Y); }
        }

        public void LinearMove() {
            double preD = -1;
            if (_waypoint.HasValue)
                preD = Geometrics.Distance(PositionVector, _waypoint.Value);

            RealPosition.X = RealPosition.X + Velocity.X * (float)BFGame.CurrentTime.ElapsedGameTime.TotalSeconds;
            RealPosition.Y = RealPosition.Y + Velocity.Y * (float)BFGame.CurrentTime.ElapsedGameTime.TotalSeconds;

            Position.X = (int)RealPosition.X;
            Position.Y = (int)RealPosition.Y;

            if (_waypoint.HasValue) {
                double aD = Geometrics.Distance(PositionVector, _waypoint.Value);
                if (Math.Abs(aD) < 0.001 || preD < aD) {
                    FixLocation((int)_waypoint.Value.X, (int)_waypoint.Value.Y);
                    _waypoint = null;
                    Velocity = new Vector2(0);
                }
            }
        }

        public void SetWaypoint(Vector2 waypoint, double velocity){
            _waypoint = waypoint;
            Vector2 temp = waypoint - PositionVector;
            temp.Normalize();
            temp = temp * (float) velocity;
            Velocity = new Vector2(temp.X, temp.Y);
        }

        public bool IsReachedWaypoint() {
            return _waypoint == null;
        }

        public Vector4 AirlineToWaypoint {
            get {
                if (_waypoint.HasValue)
                    return new Vector4(OnScreenX, OnScreenY, _waypoint.Value.X, _waypoint.Value.Y);
                else
                    return new Vector4(OnScreenX, OnScreenY, OnScreenX, OnScreenY);
            }
        }

        /// <summary>
        /// change the location to a fix position
        /// </summary>
        /// <param name="fix"></param>
        public void FixLocation(Rectangle fix) {
            Position = fix;
            RealPosition.X = Position.X;
            RealPosition.Y = Position.Y;
        }

        /// <summary>
        /// change the location to a fix position
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void FixLocation(int x, int y) {
            Position.X = x;
            Position.Y = y;
            RealPosition.X = Position.X;
            RealPosition.Y = Position.Y;
        }

        /// <summary>
        /// return true if the location rectangle contains the given point 
        /// <code>
        ///     *******************            *******************
        ///     *                 *            *                 *
        ///     *                 *            *                 *  
        ///     *                 * : true     *                 *    : false
        ///     *   #             *            *                 *
        ///     *                 *            *                 * #
        ///     *******************            *******************
        /// </code>
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public bool Contains(int x, int y) {
            return Position.Contains(x, y);
        }

        public bool IsInside(Rectangle bounds) {
            return bounds.Contains(this.Position);
        }

        /// <summary>
        /// will cause the location to be positioned near the closest edges of the given bounds (inside the bounds)
        /// <code>
        /// 
        ///     *******************            *******************
        ///     *                 *            *                 *
        ///     *                 *            *                 *
        ///     *                 *    =>      *                 *
        ///     *   #             *            *                 *
        ///     *                 *            *#                *
        ///     *******************            *******************
        /// 
        /// </code>
        /// </summary>
        /// <param name="bounds"></param>
        public void SnapToInnerEdgesOf(Rectangle bounds) {
            float distFromTop = Math.Abs(bounds.Y - Position.Y);
            float distFromBottom = Math.Abs((bounds.Y + bounds.Height) - (Position.Y + Position.Height));
            float distFromLeft = Math.Abs(bounds.X - Position.X);
            float distFromRight = Math.Abs((bounds.X + bounds.Width) - (Position.X + Position.Width));


            if (distFromBottom < distFromTop) {
                Position.Y = bounds.Y + bounds.Height - Position.Height;
            } else {
                Position.Y = bounds.Y;
            }

            if (distFromRight < distFromLeft) {
                Position.X = bounds.X + bounds.Width - Position.Width;
            } else {
                Position.X = bounds.X;
            }

            FixLocation(Position);
        }

        /// <summary>
        /// use this method if the location is out of the bounds and you want to push it into the bounds 
        /// <code>
        /// 
        ///     *******************            *******************
        ///     *                 *            *                 *
        ///     *                 *            *                 *
        ///     *                 *    =>      *                 *
        ///    #*                 *            *#                *
        ///     *                 *            *                 *
        ///     *******************            *******************
        /// 
        /// </code>
        /// </summary>
        /// <param name="bounds"></param>
        public void PushInto(Rectangle bounds) {
            if (Position.X < bounds.X)
                Position.X = bounds.X;
            if (Position.X + Position.Width > bounds.X + bounds.Width)
                Position.X = bounds.X + bounds.Width - Position.Width;
            if (Position.Y < bounds.Y)
                Position.Y = bounds.Y;
            if (Position.Y + Position.Height > bounds.Y + bounds.Height)
                Position.Y = bounds.Y + bounds.Height - Position.Height;

            FixLocation(Position);
        }

        /// <summary>
        /// return new location that is right to this location
        /// </summary>
        /// <param name="dist"></param>
        /// <returns></returns>
        public Location RightTo(int dist) {
            return new Location(Position.X + dist + Position.Width, Position.Y, Position.Width, Position.Height);
        }

        /// <summary>
        /// return new location where it x value is shifted left by the given value
        /// </summary>
        /// <param name="shift"></param>
        /// <returns></returns>
        public Location ShiftRight(int shift) {
            return new Location(Position.X + shift, Position.Y, Position.Width, Position.Height);
        }


        public Location CropLeft(int amount) {
            return new Location(Position.X + amount, Position.Y, Position.Width - amount, Position.Height);
        }

        public Location Shrink(int amount) {
            return new Location(Position.X + amount, Position.Y + amount, Position.Width - 2 * amount, Position.Height - 2 * amount);
        }


    }
}
